<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>离线语音交互系统</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
    <style>
        :root {
            --primary-color: #4361ee;
            --danger-color: #dc3545;
        }
        .recording-btn {
            width: 60px;
            height: 60px;
            border-radius: 50%;
            background-color: var(--primary-color);
            transition: all 0.3s;
        }
        .recording-btn.active {
            background-color: var(--danger-color);
            transform: scale(1.1);
        }
        @keyframes pulse {
            0% { box-shadow: 0 0 0 0 rgba(220,53,69,0.5); }
            70% { box-shadow: 0 0 0 15px rgba(220,53,69,0); }
            100% { box-shadow: 0 0 0 0 rgba(220,53,69,0); }
        }
        .pulse-animation {
            animation: pulse 1.5s infinite;
        }
    </style>
</head>
<body>
    <div class="container py-4">
        <div class="card shadow-lg">
            <div class="card-header bg-primary text-white">
                <h3 class="mb-0">离线语音交互系统</h3>
            </div>
            
            <div class="card-body">
                <div class="text-center mb-4">
                    <div id="recordingBtn" class="recording-btn d-inline-flex align-items-center justify-content-center">
                        <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" class="bi bi-mic" viewBox="0 0 16 16">
                            <path d="M3.5 6.5A.5.5 0 0 1 4 7v1a4 4 0 0 0 8 0V7a.5.5 0 0 1 1 0v1a5 5 0 0 1-4.5 4.975V15h3a.5.5 0 0 1 0 1h-7a.5.5 0 0 1 0-1h3v-2.025A5 5 0 0 1 3 8V7a.5.5 0 0 1 .5-.5z"/>
                            <path d="M10 8a2 2 0 1 1-4 0V3a2 2 0 1 1 4 0v5zM8 0a3 3 0 0 0-3 3v5a3 3 0 0 0 6 0V3a3 3 0 0 0-3-3z"/>
                        </svg>
                    </div>
                    <div class="mt-2" id="status">点击并按住麦克风按钮开始说话</div>
                </div>

                <div class="mb-3">
                    <textarea id="result" class="form-control" rows="3" 
                              placeholder="识别结果将显示在这里..." readonly></textarea>
                </div>
            </div>
        </div>
    </div>

    <script src="vosk.js"></script>
    <script>
        let recognizer = null;
        let audioContext = null;
        let mediaStream = null;

        // 初始化语音识别
        async function initVosk() {
            try {
                const model = await Vosk.createModel('models/vosk-model-small-zh-cn-0.22');
                recognizer = new model.KaldiRecognizer();
                
                recognizer.on('result', (text) => {
                    const result = JSON.parse(text.data).result;
                    if (result) {
                        const transcript = result.map(entry => entry.word).join(' ');
                        document.getElementById('result').value += transcript + '\n';
                    }
                });
                
                console.log('Vosk初始化完成');
            } catch (error) {
                console.error('初始化失败:', error);
                alert('请确保模型文件正确放置于models目录！');
            }
        }

        // 开始录音
        async function startRecording() {
            try {
                if (!recognizer) await initVosk();
                
                audioContext = new AudioContext();
                mediaStream = await navigator.mediaDevices.getUserMedia({ audio: true });
                
                const source = audioContext.createMediaStreamSource(mediaStream);
                const processor = audioContext.createScriptProcessor(4096, 1, 1);
                
                source.connect(processor);
                processor.connect(audioContext.destination);
                
                processor.onaudioprocess = (e) => {
                    if (recognizer) {
                        recognizer.acceptWaveform(e.inputBuffer);
                    }
                };

                updateUI(true);
            } catch (error) {
                console.error('录音错误:', error);
                alert('请允许麦克风访问权限！');
            }
        }

        // 停止录音
        function stopRecording() {
            if (mediaStream) {
                mediaStream.getTracks().forEach(track => track.stop());
            }
            if (audioContext) {
                audioContext.close();
            }
            updateUI(false);
        }

        // 更新界面状态
        function updateUI(recording) {
            const btn = document.getElementById('recordingBtn');
            const status = document.getElementById('status');
            
            if (recording) {
                btn.classList.add('active', 'pulse-animation');
                status.textContent = '正在录音...松开按钮停止';
            } else {
                btn.classList.remove('active', 'pulse-animation');
                status.textContent = '点击并按住麦克风按钮开始说话';
            }
        }

        // 事件绑定
        document.getElementById('recordingBtn').addEventListener('mousedown', startRecording);
        document.getElementById('recordingBtn').addEventListener('mouseup', stopRecording);
        document.getElementById('recordingBtn').addEventListener('touchstart', (e) => {
            e.preventDefault();
            startRecording();
        });
        document.getElementById('recordingBtn').addEventListener('touchend', stopRecording);
    </script>
</body>
</html>